import PropertiesReader from "properties-reader";
import fs from 'fs';
import path from 'path';
import os from 'os';
import process from "process";
import {createLogger} from "./Logger.js";


export class OpsFile {
    constructor(opsFilePath, logger) {
        this.logger = logger;
        this.opsFilePath = opsFilePath;
        this.opsFileReader = PropertiesReader(opsFilePath);
    }


    /**
     * 读取 当前目录配置文件
     * @param key 当前配置文件中的key
     * @returns {Promise<*>}
     */
    read(key) {
        let value = this.opsFileReader.get(key);
        this.logger.log(`读取配置 key:${key} value:${value}`)
        return value;
    }


    /**
     * 写入项目配置
     * @param key 键
     * @param value 值
     * @param append 是否追加（用英文逗号分隔）
     */
    async write(key, value, append = false) {
        const newValueRaw = value === undefined || value === null ? '' : String(value);
        const newValue = newValueRaw.trim();
        const oldValue = this.opsFileReader.get(key);
        let finalValue = newValueRaw;

        if (append && oldValue !== undefined && oldValue !== null && oldValue !== '') {
            const oldValueStr = String(oldValue);
            const oldValueArray = oldValueStr.split(',').map(item => item.trim());
            if (!oldValueArray.includes(newValue)) {
                finalValue = oldValueArray.length > 0 ? `${oldValueStr},${newValueRaw}` : newValueRaw;
            } else {
                finalValue = oldValueStr;
            }
        }

        this.opsFileReader.set(key, finalValue);
        await this.saveSafely(`写入配置 key:${key}`);
    }

    /**
     * 仅当 key 不存在时写入配置
     * @param key 键
     * @param value 值
     */
    async writeIfAbsent(key, value) {
        let oldValue = this.opsFileReader.get(key);
        if (oldValue !== undefined && oldValue !== null && oldValue !== '') {
            this.logger.log(`配置已存在，跳过写入 key:${key} value:${oldValue}`);
            return;
        }
        this.opsFileReader.set(key, value);
        await this.saveSafely(`写入配置 key:${key}`);
    }

    /**
     * 写入 数字列表（逗号分隔）
     * @param key
     * @param array
     */
    async writeList(key, array) {
        const value = array.join(',');
        this.opsFileReader.set(key, value);
        await this.saveSafely(`写入列表 key:${key} value:${value}`);
    }

    /**
     * 持久化到磁盘，并在失败时给出可读错误
     * @param context 保存场景描述
     */
    async saveSafely(context) {
        try {
            await this.opsFileReader.save(this.opsFilePath);
        } catch (err) {
            const reason = err?.message || err;
            this.logger.error(`${context} 失败，文件：${this.opsFilePath}，原因：${reason}`);
            throw new Error(`写入配置文件失败，请检查路径或权限：${this.opsFilePath}`);
        }
    }

    /**
     * 读取  数字列表（逗号分隔）
     * @param key
     * @returns {number[]|*[]}
     */
    readNumberList(key) {
        const value = this.opsFileReader.get(key);
        return value ? value.split(',').map(v => Number(v.trim())) : [];
    }
    /**
     * 读取  数字列表（逗号分隔）
     * @param key
     * @returns {String[]|*[]}
     */
    readList(key) {
        const value = this.opsFileReader.get(key);
        return value ? value.split(',') : [];
    }

    /**
     * 写入 json 对象 项目
     * @param key
     * @param obj
     */
    async writeJsonObject(key, obj) {
        const value = JSON.stringify(obj);
        this.opsFileReader.set(key, value);
        await this.saveSafely(`写入 JSON 配置 key:${key}`);
    }

    /**
     * 读取 json 对象
     * @param key
     * @returns {{}|any|{}|{}}
     */
    readJsonObject(key) {
        const value = this.opsFileReader.get(key);
        try {
            const parsed = value ? JSON.parse(value) : {};
            return typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
        } catch (e) {
            this.logger.error(`解析 JSON 对象失败 key:${key} error:${e.message}`);
            return {};
        }
    }

    /**
     * 写入 json 数组
     * @param key
     * @param arr
     */
    async writeJsonArray(key, arr) {
        const value = JSON.stringify(arr);
        this.opsFileReader.set(key, value);
        await this.saveSafely(`写入 JSON 数组 key:${key}`);
    }

    /**
     * 读取 json 数组
     * @param key
     * @returns {any[]|*[]}
     */
    readJsonArray(key) {
        const value = this.opsFileReader.get(key);
        try {
            const parsed = value ? JSON.parse(value) : [];
            return Array.isArray(parsed) ? parsed : [];
        } catch (e) {
            this.logger.error(`解析 JSON 数组失败 key:${key} error:${e.message}`);
            return [];
        }
    }
}

/**
 * 获取文件读取的对象
 * 项目维度，不能为空，不会初始化
 */
export function getProjectOpsFile() {
    const logger = createLogger('项目文件');
    const opsPropertiesPath = findNearestOpsConf(process.cwd());
    if (!opsPropertiesPath) {
        throw new Error(`【错误】项目配置文件不存在，请在包含 ops.conf 的目录执行，当前路径：${process.cwd()}`);
    }
    return new OpsFile(opsPropertiesPath, logger);
}

/**
 * 获取文件读取的对象
 * 项目维度，不能为空，不会初始化
 */
export function getProjectOpsFileV2() {
    const logger = createLogger("项目文件");

    const parentDir = path.dirname(process.cwd());
    const opsPropertiesPath = findNearestOpsConf(parentDir);
    if (!opsPropertiesPath) {
        throw new Error(`【错误】项目配置文件不存在，请在包含 ops.conf 的目录执行，当前路径：${parentDir}`);
    }

    return new OpsFile(opsPropertiesPath, logger);
}

/**
 * 创建项目文件
 * 项目维度，不能为空，不会初始化
 */
export function initProjectOpsFile(directory) {
    const logger = createLogger('项目文件');
    const opsPropertiesPath = path.join(directory, 'ops.conf');
    ensureFileExists(opsPropertiesPath, logger)
    if (!fs.existsSync(opsPropertiesPath)) {
        throw new Error(`【错误】项目配置文件不存在：${opsPropertiesPath}，请在发布单项目文件夹下执行该指令`);
    }
    return new OpsFile(opsPropertiesPath, logger);
}

/**
 * 获取文件读取的对象
 */
export function getGlobalOpsFile() {
    const logger = createLogger('全局配置文件');
    const opsHomeDir = resolveOpsHomeDir();
    const opsGlobalPath = path.join(opsHomeDir, 'ops_global.properties');

    if (!fs.existsSync(opsGlobalPath)) {
        throw new Error(`【错误】全局配置文件不存在 ${opsGlobalPath} 请先执行初始化脚本`);
    }

    return new OpsFile(opsGlobalPath, logger);
}
/**
 * 项目信息缓存文件
 */
export function initGlobalProjectOpsFile() {
    const logger = createLogger('项目缓存文件');
    const opsHomeDir = resolveOpsHomeDir();
    const opsGlobalProjectPath = path.join(opsHomeDir, 'project_global.conf');

    // 确保文件存在（不存在会创建空文件）
    ensureFileExists(opsGlobalProjectPath, logger);

    if (!fs.existsSync(opsGlobalProjectPath)) {
        throw new Error(`【错误】全局项目缓存文件不存在 ${opsGlobalProjectPath}`);
    }

    return new OpsFile(opsGlobalProjectPath, logger);
}

/**
 * 递归向上查找 ops.conf 文件
 * @param {string} startDir 起始目录
 * @returns {string|null}
 */
function findNearestOpsConf(startDir) {
    let dir = startDir;

    while (dir && dir !== path.parse(dir).root) {
        const candidate = path.join(dir, 'ops.conf');
        if (fs.existsSync(candidate)) {
            return candidate;
        }
        const parentDir = path.dirname(dir);
        if (parentDir === dir) {
            break;
        }
        dir = parentDir;
    }

    const rootCandidate = path.join(dir, 'ops.conf');
    return fs.existsSync(rootCandidate) ? rootCandidate : null;
}

/**
 * 获取 OPS 根目录（~/ops）
 * @returns {string}
 */
const DACS_PATH_REGEX = /^([a-zA-Z]:)?\/meili\/[^/]+\/103(\/|$)/;
const WIN_DOMAIN_DRIVE_REGEX = /^([op]):\//i;

function detectDacsOpsHomeDir(currentDir) {
    const normalized = (currentDir || '').replace(/\\/g, '/');
    const domainDriveMatch = process.platform === 'win32' ? normalized.match(WIN_DOMAIN_DRIVE_REGEX) : null;
    const domainDrivePrefix = domainDriveMatch ? `${domainDriveMatch[1].toUpperCase()}:` : null;
    const isWinDomainDrive = Boolean(domainDrivePrefix);
    if (!DACS_PATH_REGEX.test(normalized) && !isWinDomainDrive) {
        return null;
    }
    const segments = normalized.split('/').filter(Boolean);
    const meiliIndex = segments.indexOf('meili');
    if (meiliIndex === -1) {
        if (isWinDomainDrive) {
            // Windows 域内 O/P 盘挂载，直接使用 <盘符>:\ops
            return {opsHomeDir: path.join(`${domainDrivePrefix}\\`, 'ops')};
        }
        return null;
    }
    const dacsUser = segments[meiliIndex + 1];
    const envMarker = segments[meiliIndex + 2];
    if (!dacsUser || envMarker !== '103') {
        if (isWinDomainDrive) {
            return {opsHomeDir: path.join(`${domainDrivePrefix}\\`, 'ops')};
        }
        return null;
    }
    const hasLeadingSlash = normalized.startsWith('/');
    const prefixParts = segments.slice(0, meiliIndex + 3);
    const prefixPath = `${hasLeadingSlash ? '/' : ''}${prefixParts.join('/')}`;
    const opsHomeDir = path.join(prefixPath, 'Documents', 'ops');
    return {opsHomeDir};
}

export function resolveOpsHomeDir() {
    const dacsContext = detectDacsOpsHomeDir(process.cwd());
    if (dacsContext) {
        return dacsContext.opsHomeDir;
    }
    const homeDir = os.homedir();
    return path.join(homeDir, 'ops');
}

/**
 * 确保路径存在：如果目录不存在则创建，文件不存在则新建
 * @param {string} filePath 目标文件路径
 * @param logger 日志打印
 */
export function ensureFileExists(filePath, logger) {
    // 1. 先确保目录存在
    const dir = path.dirname(filePath);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, {recursive: true});
        logger.info(`目录已创建: ${dir}`);
    }
    // 2. 再确保文件存在
    if (!fs.existsSync(filePath)) {
        fs.writeFileSync(filePath, '', 'utf-8');
        logger.info(`文件已创建: ${filePath}`);
    }
}
